//
//  MMNetWork.swift
//  movieInfo
//
//  Created by zhangkai on 2023/5/13.
//

import UIKit

enum MovieInfoEndPoint: String, CaseIterable {
    case topRated = "movie/top_rated?"
    case nowPlaying = "movie/now_playing?"
    case upcoming = "movie/upcoming?"
    case popular = "movie/popular?"
}

class MMNetWork {
    
    public static let shared = MMNetWork()
    let baseURL = "https://api.themoviedb.org/3/"
    let avatarBaseURL = "https://secure.gravatar.com/avatar/"
    let apiKey = "ebe15631323144a3c214e102fb6819ac"

    
    let cache = NSCache<NSString, UIImage>()
    
    func getSearchURL(query: String, page: Int) -> String {
        let apiKeyPart = "api_key=\(apiKey)"
        let endpoint = baseURL + "search/movie?\(apiKeyPart)&language=en-US&query=\(query)&page=\(page)&include_adult=false"
        return endpoint
    }
    
    func getMoviesURL(endpoint: MovieInfoEndPoint, page: Int) -> String {
        let apiKeyPart = "api_key=\(apiKey)"
        let endpoint = baseURL + "\(endpoint.rawValue)\(apiKeyPart)&language=en-US&page=\(page)"
        return endpoint
    }
    
    func getPersonDetailsURL(personID: Int) -> String {
        let apiKeyPart = "api_key=\(apiKey)"
        let endpoint = baseURL + "person/\(personID)?\(apiKeyPart)&language=en-US"
        print(endpoint)
        return endpoint
    }
    
    func searchMovieURL(type: String, page: Int) -> String {
        let apiKeyPart = "api_key=\(apiKey)"
        let endpoint = baseURL + "\(type)\(apiKeyPart)&language=en-US&page=\(page)"
        return endpoint
    }
    
    
    func getNewMovieURL(ID: String, page: Int) -> String {
        let apiKeyPart = "api_key=\(apiKey)"
        let endpoint = baseURL + "\("movie/\(ID)/videos?")\(apiKeyPart)&language=en-US"
        return endpoint
    }
    
    
    func getNewPersonURL(ID: String, page: Int) -> String {
        let apiKeyPart = "api_key=\(apiKey)"
        let endpoint = baseURL + "person/popular?page=\(page)&\(apiKeyPart)"
        return endpoint
    }
    
    
    func getNewWeekInfoURL(page: Int) -> String {
        let apiKeyPart = "api_key=\(apiKey)"
        let endpoint = baseURL + "trending/movie/week?language=en-US&\(apiKeyPart)&page=\(page)"
        return endpoint
    }
    
    
//    const url = 'https://api.themoviedb.org/3/trending/movie/week?language=en-US';
//

    
    
    func fetchData<T: Decodable>(urlString: String, castType: T.Type, keyPath: String? = nil, completed: @escaping (Result<T, MIError>) -> Void) {
        guard let url = URL(string: urlString) else {
            completed(.failure(.invalidUrl))
            return
        }
        
        let request = NSMutableURLRequest(url: url, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0)
        request.httpMethod = "GET"
        
        let session = URLSession.shared
        let task = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
            if let _ = error {
                completed(.failure(.unableToComplete))
                return
            }
            
            guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
                completed(.failure(.invalidResponse))
                return
            }
            
            guard let data = data else {
                completed(.failure(.invalidData))
                return
            }
            
            do {
                let decoder = JSONDecoder()
                decoder.keyDecodingStrategy = .convertFromSnakeCase
                
                if let keypath = keyPath {
                    let decodedData = try decoder.decode(T.self, from: data, keyPath: keypath)
                    completed(.success(decodedData))
                } else {
                    let decodedData = try decoder.decode(T.self, from: data)
                    completed(.success(decodedData))
                }
            } catch {
                completed(.failure(.unableToParseData))
            }
        })
        task.resume()
    }
    
    func fetchImage(from urlString: String, completed: @escaping (Result<UIImage, MIError>) -> Void) {
        let cacheKey = NSString(string: urlString)
        if let image = cache.object(forKey: cacheKey) {
            completed(.success(image))
            return
        }
        
        guard let url = URL(string: urlString) else {
            completed(.failure(.invalidUrl))
            return
        }
        
        let task = URLSession.shared.dataTask(with: url) { [weak self] (data, response, error) in
            guard let self = self else { return }
            if let _ = error {
                completed(.failure(.unableToComplete))
                return
            }
            
            guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
                completed(.failure(.invalidResponse))
                return
            }
            
            guard let data = data else {
                completed(.failure(.invalidData))
                return
            }
            
            if let image = UIImage(data: data) {
                self.cache.setObject(image, forKey: cacheKey)
                completed(.success(image))
            } else {
                completed(.failure(.unableToGetImage))
            }
        }
        task.resume()
    }
}




class MIImageView: UIImageView {
    
    private let cache = MMNetWork.shared.cache
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        style()
    }
    
    func style() {
        image = UIImage(named: "placeholder")
        translatesAutoresizingMaskIntoConstraints = false
        layer.cornerRadius = 10
        layer.masksToBounds = true
        layer.borderColor = UIColor.secondarySystemFill.cgColor
        layer.borderWidth = 5
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func setPlaceHolder() {
        image = UIImage(named: "placeholder")
    }
    
    func downloadImage(fromPath path: String) {
        let url = "https://image.tmdb.org/t/p/w500\(path)"
        MMNetWork.shared.fetchImage(from: url) { [weak self] result in
            guard let self = self else { return }
            switch result {
            case .success(let image):
                self.setImage(image: image)
            case .failure(let error):
                print("\(error): \(error.rawValue)")
            }
        }
    }
    
    private func setImage(image: UIImage) {
        DispatchQueue.main.async {
            UIView.transition(with: self, duration: 0.3, options: .transitionCrossDissolve, animations: {
                self.image = image
            })
        }
    }
}







extension JSONDecoder {
    func decode<T: Decodable>(_ type: T.Type, from data: Data, keyPath: String) throws -> T {
        let toplevel = try JSONSerialization.jsonObject(with: data)
        print(toplevel)
        if let nestedJson = (toplevel as AnyObject).value(forKeyPath: keyPath) {
            let nestedJsonData = try JSONSerialization.data(withJSONObject: nestedJson)
            return try decode(type, from: nestedJsonData)
        } else {
            throw DecodingError.dataCorrupted(.init(codingPath: [], debugDescription: "Nested json not found for key path \"\(keyPath)\""))
        }
    }
}
